import axios from 'axios'
import router from '@/router'
import { getJwtFromCookie, setJwtToCookie } from './jwt'
import { toastError } from './toast'

const service = axios.create({
  baseURL: process.env.VUE_APP_BASE_DOMAIN_API,
  // 当执行跨域请求时发送cookies
  // axios 默认不发送cookie，需要全局设置为 true 发送 cookie
  withCredentials: true,
  // 设置请求的过期时间长度为60秒（毫秒）
  timeout: 60000,
  responseType: 'json',
  headers: {
    // 设置后端需要的传参类型
    'Content-Type': 'application/json',
    'X-Requested-With': 'XMLHttpRequest',
  },
})

// 设置post请求头
service.defaults.headers.post['Content-Type'] = 'application/json'
// 'application/x-www-form-urlencoded'

// 设置全局的请求次数
service.defaults.retry = 5
// 设置全局的请求的间隙
service.defaults.retryDelay = 5000

// request interceptor 请求拦截器，发送请求前先处理一些业务
service.interceptors.request.use(
  (config) => {
    // 先从 cookie 中获取 jwt 令牌，如果有令牌，则每个请求都带上令牌
    // 如果没有令牌，则后台服务器会发回响应，则跳转到登录页面
    // 这里配合 before_guards.js 里的路由全局守卫功能实现
    const jwt = getJwtFromCookie()
    if (jwt) {
      // 让每一个请求都带上JWT令牌，这里的['authentication']是一个定制的头部key
      config.headers['authorization'] = 'Bearer ' + jwt
    } else {
      config.headers['authorization'] = ''
    }
    // console.log('request.js 发送请求时 config 是：', config)
    return config
  },
  (error) => {
    console.log('request.js 请求发生错误！', error)
    return Promise.reject(error)
  }
)

// response interceptor 响应拦截器，让 axios 处理服务器返回的数据
service.interceptors.response.use(
  (response) => {
    console.log('request.js 捕捉到服务器发回正常响应：', response)
    // 如果不存在 response.data
    if (!response.data) {
      toastError('服务器没有返回自定义的data！')
      return Promise.reject(new Error('服务器没有返回自定义的data！'))
    }
    // 如果不存在 response.data.statusCode
    // if (response.data.statusCode) {
    //   const customCode = response.data.statusCode
    //   if (customCode.startsWith('20')) return response
    //   else return Promise.reject(response.data)
    // } else {
    //   return Promise.reject('服务器错误')
    // }
    // 执行到这里说明存在 response.data，既服务器返回了自定义响应
    const customCode = response.data.statusCode
    // 如果是返回文件的二进制数据流用来下载，则需要放行
    if (!customCode && response.data) {
      // 直接返回 response，不要只返回 response.data，后面还要获取 response.header
      return response
    }

    if (customCode.startsWith('2')) {
      // 如果返回的code是以2开头，说明一切正常
      // 每次响应成功都要再比对服务器发回的jwt，因为服务器有可能会更新jwt
      const jwtNew = response.headers.authorization
      const jwtOld = getJwtFromCookie()
      if (jwtNew && jwtOld && jwtNew !== jwtOld) {
        setJwtToCookie(jwtNew)
      }
      return response
    } else if (customCode.startsWith('3') || customCode.startsWith('4') || customCode.startsWith('5')) {
      // 如果执行到了这里说明响应正确，但是状态码不是20000
      // 说明是api调用过程中 throw 了错误，会输出自定义错误枚举的json
      toastError(response.data.message)
      return Promise.reject(new Error(response.data.message))
    } else {
      return Promise.reject(new Error('未知错误！'))
    }
  },
  (error) => {
    if (error) {
      console.log('request.js 捕捉到服务器发回错误：', error)
      showError(error)
      return Promise.reject(error)
    } else {
      // 请求已发出，但是无响应，可能是断网的情况
      toastError('网络异常！')
      return Promise.reject(new Error('网络异常！'))
    }
  }
)

/**
 * 请求失败后，显示服务器返回的 error
 * @param {Object} error 请求返回的失败对象
 */
const showError = (error) => {
  if (error.message) {
    if (error.message.startsWith('Network Error')) {
      // 连不上后台Api，可能后台服务器关闭，会报 Network Error 错误信息
      toastError('服务器异常！')
    } else if (error.message.startsWith('timeout of')) {
      // 超时错误
      toastError('服务器连接超时！')
    } else if (error.message.startsWith('Request failed with status code 401')) {
      // 超时错误
      toastError('401 错误！')
    }
    return
  }
  // 优先返回 error.response.data 得到的是我们在服务器端自定义的错误响应
  if (error.response.data && error.response.data.message) {
    toastError(error.response.data.message)
    return
  }
  if (error.code) {
    if (error.code.startsWith('ERR_BAD_REQUEST')) {
      toastError('API服务器异常！')
      return
    }
  }
  // 得到服务器的响应返回的错误，注意，这个错误不是我们自定义的错误
  if (error.response.status) {
    // 得到服务器返回的错误代码和错误信息
    const code = error.response.status
    const errMsg = error.message
    // 401 Unauthorized 未授权错误
    // 403 Forbidden 拒绝访问错误
    if (code === 401 || code === 403) {
      toastError(errMsg)
      jumpToLogin()
    } else if (code === 404) {
      toastError('404错误！')
    } else if (code === 500) {
      let errMsg
      // 如果涉及文件下载responseType blob的情况，要将blob对象转化为json
      // 文件类型调用ajax 取后端的返回值做特殊处理，然后弹窗展示
      if (error.response.config.responseType === 'blob') {
        errMsg = fileToJson(error.response.data).message
      } else if (error.response.data.code === 'RuntimeException' || error.response.data.code.indexOf('110056') === 0) {
        errMsg = error.response.data.message
      } else {
        errMsg = '系统繁忙，请稍后重试！'
      }
      toastError(errMsg)
    }
  }
}

// 将blob对象转化为json（文件类型调用ajax 取后端的返回值做特殊处理）
function fileToJson(file) {
  return new Promise((resolve, reject) => {
    const reader = new FileReader()
    // 成功回调
    reader.onload = (res) => {
      // 得到字符串
      const { result } = res.target
      // 解析成json对象
      const data = JSON.parse(result)
      resolve(data)
    }
    // 失败回调
    reader.onerror = (err) => {
      reject(err)
    }
    // 按照utf-8编码解析
    reader.readAsText(new Blob([file]), 'utf-8')
  })
}

// 跳转登录页，携带当前页面路由，在登录页面完成登录后返回当前页面
const jumpToLogin = () => {
  router.replace({
    path: '/login',
    query: {
      redirect: router.currentRoute.fullPath,
    },
  })
}

export default service
